﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;

using Autofac;

using Caliburn.Micro;

using Cooking.Client.Services;
using Cooking.Client.ViewModels;
using Cooking.Client.Views;

namespace Cooking.Client
{
    public class ClientBootstrapper : Bootstrapper<ShellViewModel>
    {
        private IContainer container;

        /// <summary>
        ///   Override to configure the framework and setup your IoC container.
        /// </summary>
        protected override void Configure()
        {
            RegisterTypes();
            AddElementConventions();
        }

        private void RegisterTypes()
        {
            var builder = new ContainerBuilder();

            builder.RegisterType<ShellViewModel>().AsSelf().As<IBusyService>().SingleInstance();

            builder.RegisterType<EventAggregator>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<CommandAgent>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<QueryAgent>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<WindowManager>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<ApplicationController>().AsImplementedInterfaces().SingleInstance();

            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).SingleInstance().AsImplementedInterfaces().AsSelf();
            
            container = builder.Build();
        }

        private static void AddElementConventions()
        {
            ConventionManager.AddElementConvention<Rating>(Rating.ValueProperty, "Value", "ValueChanged");
            ConventionManager.AddElementConvention<DataGrid>(DataGrid.ItemsSourceProperty, "DataContext", "Loaded");
        }

        /// <summary>
        /// Override this to provide an IoC specific implementation.
        /// </summary>
        /// <param name="serviceType">The service to locate.</param>
        /// <param name="key">The key to locate.</param>
        /// <returns>The located service.</returns>
        protected override object GetInstance(Type serviceType, string key)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                if (container.IsRegistered(serviceType))
                {
                    return container.Resolve(serviceType);
                }
            }
            else
            {
                if (container.IsRegisteredWithName(key, serviceType))
                {
                    container.ResolveNamed(key, serviceType);
                }
            }

            throw new Exception(string.Format("Could not locate any instances of contract {0}.", key ?? serviceType.Name));
        }

        /// <summary>
        /// Override this to provide an IoC specific impelentation
        /// </summary>
        /// <param name="service">The service to locate.</param>
        /// <returns>The located services.</returns>
        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return (IEnumerable<object>)container.Resolve(typeof (IEnumerable<>).MakeGenericType(service));
        }

        /// <summary>
        /// Override this to provide an IoC specific implementation.
        /// </summary>
        /// <param name="instance">The instance to perform injection on.</param>
        protected override void BuildUp(object instance)
        {
            container.InjectUnsetProperties(instance);
        }

        protected override void OnUnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            // If the app is running outside of the debugger then report the exception using
            // a ChildWindow control.
            if (!Debugger.IsAttached)
            {
                // NOTE: This will allow the application to continue running after an exception has been thrown
                // but not handled. 
                // For production applications this error handling should be replaced with something that will 
                // report the error to the website and stop the application.
                e.Handled = true;
                ChildWindow errorWin = new ErrorWindow(e.ExceptionObject);
                errorWin.Show();
            }
        }
    }
}